package com.allwees.bs.c.module.ops.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.allwees.bs.c.module.ops.dao.UserCouponDao;
import com.allwees.bs.c.module.ops.entity.CouponDefEntity;
import com.allwees.bs.c.module.ops.repository.CouponDefRepository;
import com.allwees.bs.c.module.ops.service.UserCouponService;
import com.allwees.bs.c.module.ops.util.UserCouponCodeUtil;
import com.allwees.bs.c.module.user.constant.EUserCouponQueryType;
import com.allwees.bs.c.module.user.dto.UserCouponDTO;
import com.allwees.bs.c.module.user.entity.UserCouponEntity;
import com.allwees.bs.c.module.user.entity.UserEntity;
import com.allwees.bs.c.module.user.repository.UserCouponRepository;
import com.allwees.bs.c.module.user.service.IUserService;
import com.allwees.bs.core.model.coupon.constant.ECouponUseType;
import com.allwees.bs.core.modelbase.constant.ResultEnum;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import com.allwees.bs.core.modelbase.page.PageParams;
import com.allwees.bs.core.modelbase.page.PaginateDTO;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author mouhaining
 * @since 2002-05-19 17:06
 */
@Service
@Slf4j
public class UserCouponServiceImpl extends ServiceImpl<UserCouponDao,UserCouponEntity> implements UserCouponService {

    @Autowired
    private IUserService userService;

    @Autowired
    private CouponDefRepository couponDefRepository;

    @Autowired
    private UserCouponRepository userCouponRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCouponEntity use(String couponUuid) {
        UserCouponEntity userCoupon = checkUserCouponDef(couponUuid);
        userCoupon.setStatus(EStatus.USED.value());
        this.updateById(userCoupon);
        return userCoupon;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCouponEntity receiveAndUse(String userUuid, String no) {
        UserCouponEntity userCoupon = userCouponRepository.findByUserUuidAndNo(userUuid, no);
        if (userCoupon == null) {
            return null;
        }

        if (!EStatus.NORMAL.value().equals(userCoupon.getStatus())) {
            throw BusinessException.getInstance(ResultEnum.COUPON_EXPIRED);
        }

        if (DateUtil.nowTime() > userCoupon.getOverdueTime().getTime()) {
            throw BusinessException.getInstance(ResultEnum.COUPON_EXPIRED);
        }
        userCoupon.setStatus(EStatus.USED.value());
        this.updateById(userCoupon);
        return userCoupon;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCouponEntity signIn(String userUuid,ECouponUseType couponUseType) {
        UserEntity user = userService.findByUserUuid(userUuid);
        if (user == null) {
            return null;
        }
        return receive(userUuid, couponUseType);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void inviteeRegistered(String userUuid) {
        UserEntity inviter = userService.findInviterUser(userUuid);
        if (inviter == null) {
            return;
        }
        receive(inviter.getUuid(), ECouponUseType.SYSTEM_INVITEE_REGISTER);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCouponEntity newRegisteredUserGet(String userUuid) {
        UserEntity user = userService.findByUserUuid(userUuid);
        if (user == null) {
            return null;
        }
        return receive(userUuid, ECouponUseType.SYSTEM_REGISTERED_PICK);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCouponEntity receive(String userUuid, String couponUuid) {
        CouponDefEntity coupon = checkCouponDef(couponUuid);
        return doReceive(userUuid, coupon);
    }

    private UserCouponEntity receive(String userUuid, ECouponUseType useType) {
        CouponDefEntity coupon = couponDefRepository.findByUseType(useType);
        return doReceive(userUuid, coupon);
    }


    private UserCouponEntity doReceive(String userUuid, CouponDefEntity coupon){
        if (coupon == null) {
            return null;
        }
        if(coupon.getReserve() <= 0){
            log.warn("coupon count not enough,ECouponUseType:{},userUuid:{}",coupon.getUseType(),userUuid);
            return null;
        }
        List<UserCouponEntity> userCoupons = userCouponRepository.findByUserUuidAndCouponUuid(userUuid, coupon.getUuid());
        Integer maxCount = coupon.getSingleUserReceiveMaxCount();
        if (maxCount != null && maxCount <= userCoupons.size()) {
            log.warn("receive count exceeded max count,userUuid:{},ECouponUseType:{}",userUuid,coupon.getUseType());
            return null;
        }

        UserCouponEntity userCoupon = ofUserCoupon(userUuid, DateUtil.now(), coupon);
        this.save(userCoupon);
        String userCouponNo = UserCouponCodeUtil.idToCode(userCoupon.getId());
        this.lambdaUpdate()
                .eq(UserCouponEntity::getId,userCoupon.getId())
                .set(UserCouponEntity::getNo, userCouponNo)
                .update();

        couponDefRepository.incReceivedCount(coupon.getUuid());
        userCoupon.setNo(userCouponNo);
        return userCoupon;
    }


    @Override
    public UserCouponEntity load(String uuid) {
        UserCouponEntity entity = userCouponRepository.getByUuid(uuid);
        Assert.notNull(entity, "UserCoupon [" + uuid + "] is null");
        return entity;
    }

    @Override
    public List<UserCouponDTO> getCanUseCoupons(String userUuid) {
        List<UserCouponEntity> userCoupons = userCouponRepository.findCanUse(userUuid, DateUtil.now());
        if (CollectionUtils.isEmpty(userCoupons)) {
            return new ArrayList<>(0);
        }
        List<UserCouponDTO> dtos = new ArrayList<>(userCoupons.size());
        userCoupons.forEach(o -> dtos.add(new UserCouponDTO(o)));
        return dtos;
    }

    @Override
    public PaginateDTO<UserCouponDTO> page(String userUuid, EUserCouponQueryType queryType, PageParams pageParams) {
        Page<UserCouponEntity> page = null;
        if (EUserCouponQueryType.AVAILABLE.equals(queryType)) {
            page = userCouponRepository.findAvailable(userUuid, DateUtil.truncateToDay(), pageParams.toPageable());
        } else if (EUserCouponQueryType.USED.equals(queryType)) {
            page = userCouponRepository.findUsed(userUuid, pageParams.toPageable());
        } else if (EUserCouponQueryType.EXPIRED.equals(queryType)) {
            page = userCouponRepository.findExpired(userUuid, DateUtil.truncateToDay(), pageParams.toPageable());
        } else if (EUserCouponQueryType.ALL.equals(queryType)) {
            page = userCouponRepository.findByUserUuid(userUuid, pageParams.toPageable());
        }

        if (page == null || page.getTotalElements() == 0) {
            return PaginateDTO.of(0, 0, pageParams);
        }

        List<UserCouponDTO> dtos = new ArrayList<>(page.getContent().size());
        page.getContent().forEach(o -> dtos.add(new UserCouponDTO(o)));

        PaginateDTO<UserCouponDTO> page1 = PaginateDTO.of(page.getTotalElements(), page.getContent().size(), pageParams);
        return page1.setResults(dtos);
    }

    @Override
    public void completed(String userCouponUuid) {
        UserCouponEntity userCoupon = userCouponRepository.getByUuid(userCouponUuid);
        if (!EStatus.USED.equals(userCoupon.getEStatus())) {
            return;
        }
        userCoupon.setEStatus(EStatus.COMPLETED);
        this.updateById(userCoupon);
    }

    @Override
    public UserCouponEntity getRegisteredCoupon(String userUuid) {
        UserCouponEntity entity = new UserCouponEntity();
        entity.setUserUuid(userUuid);
        entity.setCouponUseType(ECouponUseType.SYSTEM_REGISTERED_PICK);
        return userCouponRepository.findOne((Example.of(entity))).orElse(null);
    }

    private CouponDefEntity checkCouponDef(String couponUuid) {
        CouponDefEntity coupon = couponDefRepository.getByUuid(couponUuid);
        if (coupon == null) {
            throw BusinessException.getInstance(ResultEnum.COUPON_NOT_FOUND);
        }
        if (!EStatus.NORMAL.value().equals(coupon.getStatus())) {
            throw BusinessException.getInstance(ResultEnum.COUPON_EXPIRED);
        }
        return coupon;
    }

    private UserCouponEntity checkUserCouponDef(String couponUuid) {
        UserCouponEntity userCoupon = userCouponRepository.getByUuid(couponUuid);
        if(userCoupon == null){
            throw BusinessException.getInstance(ResultEnum.COUPON_NOT_FOUND);
        }
        if (!EStatus.NORMAL.value().equals(userCoupon.getStatus())) {
            throw BusinessException.getInstance(ResultEnum.USER_COUPON_USED);
        }
        if (DateUtil.nowTime() > userCoupon.getOverdueTime().getTime()) {
            throw BusinessException.getInstance(ResultEnum.COUPON_EXPIRED);
        }
        return userCoupon;
    }

    private static UserCouponEntity ofUserCoupon(String userUuid, Date receiveDate, CouponDefEntity coupon) {
        UserCouponEntity userCoupon = new UserCouponEntity(coupon, receiveDate);
        userCoupon.setUserUuid(userUuid);
//        userCoupon.setPromoCode(promoCode);
        return userCoupon;
    }
}


























